﻿using System;
using System.ComponentModel;
using System.Linq;

namespace N2.Workflow
{
	using Items;

	partial class WorkflowProvider
	{
		static bool TransferCommonPropertiesFromTransientState(IItemState source, ItemState target)
		{
			var _syncProps = "Comment,SavedBy,AssignedTo,Created".Split(',');

			var _properties =
				from _prop in TypeDescriptor.GetProperties(source).Cast<PropertyDescriptor>()
				join _name in _syncProps
					on _prop.Name equals _name
				select _prop;
			
			_properties
				.Where(_prop => null != _prop.GetValue(source) || _prop.GetValue(source) != _prop.GetValue(target));
			
			if (!_properties.Any()) {
				return false;
			}
			
			_properties.ToList().ForEach(_prop => _prop.SetValue(target, _prop.GetValue(source)));
			
			return true;
		}

		static bool AssignPhysicalProperties(ItemState target, IItemState currentState, IActionDefinition action)
		{
			bool _targetModified = false;
			
			if (currentState is ItemState && null == target.PreviousState) {
				target.PreviousState = currentState as ItemState;
				_targetModified = true;
			}

			if (null == target.Action) {
				target.Action = action as ActionDefinition;
				_targetModified = true;
			}

			if (null == target.Definition) {
				target.Definition = action.LeadsTo as StateDefinition;
				_targetModified = true;
			}

			return _targetModified;
		}

		/// <summary>
		/// <c cref="System.Web.Routing.RouteValueDictionary"/>.AddValues(object values)
		/// </summary>
		/// <param name="source"></param>
		/// <param name="target"></param>
		/// <returns></returns>
		static bool AssignMatchingPropertiesFromAnonymousSource(object source, ItemState target)
		{
			var _compatiblePropertyPairs =
				from _sourceProperty in TypeDescriptor.GetProperties(source).Cast<PropertyDescriptor>()
				from _targetProperty in TypeDescriptor.GetProperties(target).Cast<PropertyDescriptor>()
				where string.Equals(_sourceProperty.Name, _targetProperty.Name, StringComparison.OrdinalIgnoreCase)
				let _rValue = _sourceProperty.GetValue(source)
				where _targetProperty.PropertyType.IsAssignableFrom(_sourceProperty.PropertyType)
				select new {
					Source = _sourceProperty,
					Target = _targetProperty };

			var _nonEqualPropertyPairs =
				from _pair in _compatiblePropertyPairs
				let _lValue = _pair.Target.GetValue(target)
				let _rValue = _pair.Source.GetValue(source)
				where _lValue != _rValue
				where null != _rValue
				select new {
					Target = _pair.Target,
					Value = _rValue,
				};

			if (!_nonEqualPropertyPairs.Any()) {
				return false;
			}

			_nonEqualPropertyPairs
				.ToList()
				.ForEach(_pair => _pair.Property.SetValue(target, _pair.Value));

			return true;
		}

		static bool IsActionValidForState(IStateDefinition currentState, IActionDefinition action)
		{
			return
				currentState
					.Actions
					.Any(_a => string.Equals(
						_a.Name,
						action.Name,
						StringComparison.OrdinalIgnoreCase));
		}

		void PersistItemState(ContentItem item, ItemState state)
		{
			using (var _tx = this.persister.Repository.BeginTransaction()) {
				if (state.ID <= 0) {
					this.persister.Save(state);
				}

				AssignCurrentState(item, state);
				this.persister.Save(item);
				_tx.Commit();
			}
		}

		/// <summary>
		/// Attempts to retrieve a missing Action, PreviousState and Definition
		/// properties, using some heuristics.
		/// </summary>
		/// <param name="state"></param>
		void FixLinks(ItemState state)
		{
			if (null == state.Definition) {
				if (null == state.PreviousState
						&& 1 == this.GetHistory(state.EnclosingItem).Count()) {
					var _wf = this.GetDefinition(state.EnclosingItem);
					state.Definition = _wf.InitialState;
					this.persister.Save(state);
				}
			}
		}

		static void AssignCurrentState(ContentItem item, ItemState state)
		{
			item["_CurrentState"] = state;
		}

		//try to adopt the last available state as current
		ItemState FindLastGoodState(ContentItem item)
		{
			return ((IWorkflowProvider)this).GetLatestState<ItemState>(item);
		}

		ItemState CreateInitialState(ContentItem item)
		{
			IWorkflowDefinition _wf = this.GetDefinition(item);

			if (null != _wf) {
				//return new TransientNewState {
				//    Action = null,
				//    PreviousState = null,
				//    Definition = _wf.InitialState,
				//    Comment = "New",
				//};
				var _state = (ItemState)definitions.CreateInstance(_wf.InitialState.StateType, item);
				_state.Definition = _wf.InitialState as StateDefinition;
				_state.Action = null;
				_state.PreviousState = null;
				_state.Comment = "new";
				return _state;
			}

			return default(ItemState);
		}
	}
}
